Globals = {};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "walk" διατρέχει όλα τα στοιχεία μιας λίστας και για κάθε στοιχείο
// καλεί κάποια function με παραμέτρους το ανά χείρας κλειδί και το αντίστοιχο
// στοιχείο της λίστας. Η μέθοδος επιστρέφει το πλήθος των στοιχείων της λίστας.

Globals.walk = function(list, callback) {
	var count = 0, i;

	for (i in list) {
		count++;
		if (callback) callback(i, list[i]);
	}

	return count;
};

// Η function "awalk" διατρέχει όλα τα στοιχεία ενός array και για κάθε στοιχείο
// καλεί κάποια function με παραμέτρους το ανά χείρας κλειδί και το αντίστοιχο
// στοιχείο του array. Η μέθοδος επιστρέφει το πλήθος των στοιχείων του array.
// Η διαφορά της με την "walk" είναι ότι διατρέχει τα στοιχεία του array με
// τη σειρά.

Globals.awalk = function(list, callback) {
	var i;

	for (i = 0; i < list.length; i++) {
		if (callback) callback(i, list[i]);
	}

	return list.length;
};

Globals.fatal = function(msg) {
	try {
		self.location = Client.server + 'error?minima=' + msg.uri();
	} finally {
		throw msg;
	}
};

// Η μέθοδος "sportCheck" ελέγχει την ορθότητα της πόρτας του server σκηνικού.
// Πρόκειται για την πόρτα στην οποία ακούει ο server σκηνικού και πρέπει να
// είναι ένα ακέραιο νούμερο πόρτας που δεν χρησιμοποιείται από άλλη δικτυακή
// διαδικασία, π.χ. 12666.

Globals.sportCheck = function() {
	if (!Globals.hasOwnProperty('sport')) Globals.sport = null;
	else if (Globals.sport === undefined) Globals.sport = null;
	if (Globals.sport === null) Globals.fatal('skiser port udefined');

	if (parseInt(Globals.sport) != Globals.sport) Globals.fatal(Globals.sport + ': invalid skiser port');
	Globals.sport = parseInt(Globals.sport);
	if (Globals.sport < 1000) Globals.fatal(Globals.sport + ': invalid skiser port number');
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Ακολουθούν functions σχετικές με ημερομηνία και ώρα.

// Η function "torams" επιστρέφει το τρέχον timestamp σε milliseconds,
// με όρους της μηχανής στην οποία εκτελείται.

Globals.torams = function() {
	return (new Date).getTime();
};

// Η function "tora" επιστρέφει το τρέχον timestamp σε seconds,
// με όρους της μηχανής στην οποία εκτελείται.

Globals.tora = function() {
	return Math.floor((new Date).getTime() / 1000);
};

// Η function "toramsServer" επιστρέφει το τρέχον timestamp
// σε milliseconds, με όρους του server. Η διαφορά ώρας μεταξύ
// server και client δίνεται στην property "timeDif" και είναι
// σε seconds.

Globals.toramsServer = function() {
	return Globals.torams() + (Client ? Client.timeDif * 1000 : 0);
};

// Η function "toraServer" επιστρέφει το τρέχον timestamp
// σε seconds, με όρους του server.

Globals.toraServer = function() {
	return Globals.tora() + (Client ? Client.timeDif : 0);
};

// Η function "pote" δέχεται ένα timestamp και εμφανίζει το χρονικό διάστημα
// μέχρι την τρέχουσα χρονική στιγμή σε πιο ανθρώπινη μορφή. Το τρέχον timestamp
// λογίζεται με όρους της μηχανής στην οποία εκτελείται.

Globals.pote = function(ts) {
	var tora = Globals.tora();
	var dif = tora - ts;
	if (dif < 60) return 'τώρα';

	if (dif < 3600) {
		var x = parseInt(dif / 60);
		return 'πριν ' + x + ' λεπτ' + (x < 2 ? 'ό' : 'ά');
	}

	if (dif < 86400) {
		var x = parseInt(dif / 3600);
		return 'πριν ' + x + ' ώρ' + (x < 2 ? 'α' : 'ες');
	}
	
	// Μετατρέπουμε το timestamp σε αντικείμενο ημερομηνίας, προκειμένου
	// να εκτυπώσουμε την ημερομηνία σε μορφή "dd/mm/yyyy".

	ts = new Date(ts * 1000);
	return Globals.mera(ts) + ', ' + Globals.ora(ts);
};

// Η function "mera" δίνει την τρέχουσα ημερομηνία στη μηχανή που τρέχει.
// Μπορούμε να δώσουμε και συγκεκριμένη ώρα ως παράμετρο.

Globals.mera = function(d) {
	if (d === undefined) d = new Date;
	var s = '';

	var x = d.getDate();
	if (x < 10) s += '0';
	s += x;
	s += '/';

	x = d.getMonth() + 1;
	if (x < 10) s += '0';
	s += x;
	s += '/'; 

	x = d.getFullYear();
	if (x < 2000) s += x;
	else {
		x %= 100;
		if (x < 10) s += '0';
		s += x;
	}

	return s;
};

// Η function "ora" δίνει την τρέχουσα ώρα στη μηχανή που τρέχει.
// Μπορούμε να δώσουμε και συγκεκριμένη ώρα ως παράμετρο.

Globals.ora = function(d) {
	if (d === undefined) d = new Date;
	var s = '';

	var x = d.getHours();
	if (x < 10) s += '0';
	s += x + ':';

	x = d.getMinutes();
	if (x < 10) s += '0';
	s += x + ':';

	x = d.getSeconds();
	if (x < 10) s += '0';
	s += x;

	return s;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
};

String.prototype.ltrim = function() {
	return this.replace(/^\s+/, '');
};

String.prototype.rtrim = function() {
	return this.replace(/\s+$/, '');
};

String.prototype.isNai = function() {
	return(this.valueOf() === 'ΝΑΙ');
};

String.prototype.isOxi = function() {
	return !this.isNai();
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η κλήση που ακολουθεί εξυπηρετεί στη λειτουργία άλλων μεθόδων.

;(function() {
	Globals.loginRE = new RegExp('^[a-zA-Z][a-zA-Z0-9_!@#=.:+-]*$');
	Globals.emailRE = new RegExp('^[a-zA-Z0-9_\.-]+\@([a-zA-Z0-9-]+\\.)+([a-zA-Z0-9]{2,4})+$');
	var ipOctet = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])';
	Globals.ipRE = new RegExp('^(?:' + ipOctet + '\\.){3}' + ipOctet + '$');
})();

// Η μέθοδος "validIp" ελέγχει ένα string ως προς το εαν είναι
// είναι δεκτό IP. Αν ναι το επιστρέφει, αλλιώς επιστρέφει κενό
// string.

String.prototype.validIp = function() {
	return this.match(Globals.ipRE) ? this + '' : '';
};

// Η μέθοδος "validEmail" ελέγχει ένα string ως email address. Αν
// είναι δεκτό το επιστρέφει, αλλιώς επιστρέφει κενό string.

String.prototype.validEmail = function() {
	return this.match(Globals.emailRE) ? this : '';
};

// Η μέθοδος "validLogin" ελέγχει ένα string ως login name. Αν
// είναι δεκτό το επιστρέφει, αλλιώς επιστρέφει κενό string.

String.prototype.validLogin = function() {
	return this.match(Globals.loginRE) ? this : '';
};

// Η μέθοδος "json" επιστρέφει json safe μορφή του ανά χείρας string.

String.prototype.json = function() {
	return Globals.json(this.valueOf());
};

// Η μέθοδος "uri το μετατρέπει ένα string σε μορφή ασφαλή
// ώστε να χρησιμοποιηθεί ως URI component.

String.prototype.uri = function() {
	return encodeURIComponent(this);
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "json" δέχεται μια παράμετρο και την μετατρέπει σε
// μορφή ασφαλή ώστε να χρησιμοποιηθεί ως rvalue σε δομές json.
// Η function δεν δίδεται ως string method ώστε να δύναται να
// χρησιμοποιηθεί σε οποιαδήποτε μεταβλητή και όχι μόνο σε strings.

Globals.json = function(s) {
	var err = false;
	if (s === undefined) err = 'undefined data';
	else if (s === null) err = 'null data';
	else {
		switch (typeof s)  {
		case 'number':
			return s;
		case 'string':
			var x = s.replace(/\\/g, '\\\\');
			x = x.replace(/[\n\r\f\v\b\t]/g, ' ');
			return "'" + x.replace(/'/g, '\\\'') + "'";
		default:
			err = s + ': invalid data type';
			break;
		}
	}

	Globals.fatal('Globals.json: ' + err);
};

// Η μέθοδος "random" επιστρέφει έναν τυχαίο ακέραιο μεταξύ των τιμών που δίνονται
// ως παράμετροι (inclusive). Π.χ. η κλήση Globals.random(5, 10) μπορεί να δώσει 5,
// 6, 7, 8, 9 και 10.

Globals.random = function(min, max) {
	switch (arguments.length) {
	case 0:
		min = 0;
		max = 999999999;
		break;
	case 1:
		max = min;
		min = 0;
		break;
	}

	return Math.floor(Math.random() * (max - min + 1)) + min;
};

// Η μέθοδος "randomString" επιστρέφει ένα τυχαίο string με μήκος που καθορίζεται
// από τις παραμέτρους. Το string αποτελείται από γράμματα του λατινικού αλφαβήτου,
// αλλά αν θέλουμε μπορούμε να περάσουμε αυθαίρετο string από το οποίο θα επιλεγούν
// χαρακτήρες.

Globals.randomString = function(min, max, pool) {
	if (pool === undefined) pool = 'abcdefghijklmnopqrstuvwxyz';
	var ret = '';
	max = Globals.random(min, max);
	for (min = 0; min < max; min++) ret += pool.substr(Globals.random(pool.length - 1), 1);
	return ret;
};

// Η function "initObj" χρησιμοποιείται συνήθως στην αρχικοποίηση αντικειμένων.
// Δέχεται ένα αντικείμενο και μια λίστα και θέτει τα στοιχεία τής λίστας ως
// properties του αντικειμένου. Αν το αντικείμενο είχε κάποια properties πριν
// την εφαρμογή τής μεθόδου, αυτά τα properties διατηρούνται. By default τα
// properties τύπου function δεν αντιγράφονται, μπορούμε όμως να αντιγράψουμε
// και αυτά δίνοντας κατάλληλες options στην τρίτη παράμετρο.

Globals.initObject = function(obj, props, opt) {
	var i;

	if (opt === undefined) opt = {
		functions: false,
		recursive: true,
	};

	for (i in props) {
		if (props[i] === null) obj[i] = null;
		else if (typeof props[i] === 'function') {
			if (opt.functions) obj[i] = props[i];
		}
		else if (typeof props[i] !== 'object') obj[i] = props[i];
		else if (opt.recursive) {
			if (props[i] instanceof Array) obj[i] = Globals.initObject([], props[i]);
			else obj[i] = Globals.initObject({}, props[i]);
		}
		else obj[i] = props[i];
	}

	return obj;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

// Η function "aferesi" αφαιρεί properties από αντικείμενα. Το προς επεξεργασία
// αντικείμενο δίδεται ως πρώτη παράμετρο και ακολουθούν οι προς αφαίρεσιν properties.
// Επιστρέφεται λίστα με τις αφαιρεθείσες properties, π.χ.
//
//	a = Globals.aferesi(trapezi, 'skiniko', 'pektis1', 'pektis2', 'pektis3');
//
// αφαιρεί από το αντικείμενο "trapezi" τις properties "skiniko", "pektis1", "pektis2"
// και "pektis3". Η λίστα "a" περιέχει όσες από αυτές τις properties βρέθηκαν και
// αφαιρέθηκαν από το "trapezi".
//
// Αργότερα μπορεί να χρησιμοποιηθεί η αντίστροφη function "iserefa" προκειμένου να
// επαναφέρουμε τις αφαιρεθείσες properties.

Globals.aferesi = function() {
	var i, alist = {};

	for (i = 1; i < arguments.length; i++) {
		Globals.aferesiPush(arguments[0], alist, arguments[i]);
	}

	return alist;
};

// Η function "aferesiPush" είναι η «ατμομηχανή» της "aferesi", καθώς είναι αυτή που
// προσθέτει properties στη λίστα αφαιρεθεισών properties. Ως παραμέτρους δέχεται το
// ίδιο το αντικείμενο, την μέχρι τούδε λίστα αφαιρεθεισών properties και το όνομα
// μιας επιπλέον property που θα αφαιρεθεί. Επιστρέφει τη λίστα μετά την ένταξη και
// της νεοαφαιρεθείσης property.

Globals.aferesiPush = function(obj, alist, prop) {
	if (obj.hasOwnProperty(prop)) {
		alist[prop] = obj[prop];
		delete obj[prop];
	}

	return alist;
};

// Η function "iserefa" επαναφέρει properties που αφαιρέθηκαν από αντικείμενα με την
// function "aferesi".

Globals.iserefa = function(obj, alist) {
	var i;

	for (i in alist) obj[i] = alist[i];
	return obj;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Debug = {
	flags: {},
};

Debug.flagSet = function(flag, val) {
	if (val === undefined) val = true;
	Debug.flags[flag] = val;
};

Debug.flagGet = function(flag) {
	return Debug.flags[flag];
};

Debug.flagSet('epomenosCheck');
Debug.flagSet('agoraEpomenosCheck');
